﻿using System;
using System.Threading;

namespace Daco.Tasks
{
    public delegate void TaskStartEventHandler(object sender, CTaskStartEventArgs e);
    public delegate void TaskCompletedEventHandler(object sender, CTaskCompletedEventArgs e);

    public interface IBackgroundTask : IDisposable
    {
        bool Cancel { get; }
        DateTime StartedDate { get; }
        bool IsRunning { get; }
        object AppContext { set; }
        // Request & reponse
        void Request(CTaskStartEventArgs e);
        void Run();
        // Event handler
        event TaskStartEventHandler TaskStart;
        event TaskCompletedEventHandler TaskCompleted;
    }

    /// <summary>
    /// Provide information when task started
    /// </summary>
    public class CTaskStartEventArgs : EventArgs
    {
        private object _info;
        public object Info
        {
            get { return _info; }
            set { _info = value; }
        }

        public CTaskStartEventArgs()
        {
        }

        public CTaskStartEventArgs(object info)
        {
            _info = info;
        }
    }

    /// <summary>
    /// Provide information when task completed
    /// </summary>
    public class CTaskCompletedEventArgs : EventArgs
    {
        private object _info;
        public object Info
        {
            get { return _info; }
        }

        public CTaskCompletedEventArgs()
        {
            _info = null;
        }
        public CTaskCompletedEventArgs(object info)
        {
            _info = info;
        }
    }

    public class CBaseBackgroundTask : IBackgroundTask
    {
        /// <summary>
        /// 
        /// </summary>
        protected CTaskCompletedEventArgs _completedEventArgs;
        /// <summary>
        /// 
        /// </summary>
        protected CTaskStartEventArgs _startEventArgs;
        /// <summary>
        /// Task start event
        /// </summary>
        public event TaskStartEventHandler TaskStart;
        /// <summary>
        /// Task completed event
        /// </summary>
        public event TaskCompletedEventHandler TaskCompleted;
        /// <summary>
        /// The lock object
        /// </summary>
        protected object _lockObject = new object();
        /// <summary>
        /// The application context
        /// </summary>
        protected object _appContext = null;
        /// <summary>
        /// 
        /// </summary>
        protected bool _IsRunning = false;
        /// <summary>
        /// 
        /// </summary>
        protected bool _cancel = false;
        /// <summary>
        /// 
        /// </summary>
        protected DateTime _startedDate;

        #region IBackgroundTask Members

        public virtual DateTime StartedDate
        {
            get { return this._startedDate; }
        }

        public virtual bool Cancel
        {
            get { return _cancel; }
        }

        public virtual bool IsRunning
        {
            get
            {
                bool IsRunning = false;
                lock (this._lockObject)
                {
                    IsRunning = this._IsRunning;
                }
                return IsRunning;
            }

            protected set
            {
                lock (this._lockObject)
                {
                    if (!this._IsRunning && value)
                    {
                        this._startedDate = DateTime.Now;
                    }
                    this._IsRunning = value;
                }
            }
        }

        public virtual object AppContext
        {
            protected get
            {
                return this._appContext;
            }
            set
            {
                this._appContext = value;
            }
        }

        public virtual void Run()
        {
            OnStart();
            IsRunning = true;
            RunOnce();
            IsRunning = false;
            OnCompleted();
        }

        public virtual void Request(CTaskStartEventArgs e)
        {
            if (_startEventArgs == null)
                _startEventArgs = new CTaskStartEventArgs();

            _startEventArgs.Info = e.Info;
        }

        #endregion

        virtual public void RunOnce()
        {

        }

        virtual protected void OnStart()
        {
            if (_startEventArgs == null) _startEventArgs = new CTaskStartEventArgs();

            if (this.TaskStart != null) this.TaskStart(this, _startEventArgs);
        }

        virtual protected void OnCompleted()
        {
            if (_completedEventArgs == null) _completedEventArgs = new CTaskCompletedEventArgs();

            if (this.TaskCompleted != null) this.TaskCompleted(this, _completedEventArgs);
        }

        #region IDisposable Members

        virtual public void Dispose()
        {
            IsRunning = false;
        }

        #endregion
    }

    public class CIntermittentBackgroundTask : CBaseBackgroundTask
    {
        /// <summary>
        /// The timer
        /// </summary>
        protected Timer _intermittentTimer = null;
        protected object _intermittentTimerSemaphore = new object();
        private long _runPeriodMs;
        private long _startDelayMs;

        public long StartDelayMs
        {
            get
            {
                return this._startDelayMs;
            }

            set
            {
                this._startDelayMs = value;
            }
        }

        /// <summary>
        /// Gets or sets RunPeriodMs.
        /// </summary>
        public long RunPeriodMs
        {
            get
            {
                return this._runPeriodMs;
            }

            set
            {
                this._runPeriodMs = value;
            }
        }

        public CIntermittentBackgroundTask()
        {
            StartDelayMs = 50;
            RunPeriodMs = Timeout.Infinite;
        }
        public CIntermittentBackgroundTask(long period, long startDelay)
        {
            _runPeriodMs = period;
            _startDelayMs = startDelay;
        }

        /// <summary>
        /// The run once.
        /// </summary>
        public override void RunOnce()
        {
            base.RunOnce();
        }

        public override void Run()
        {
            if (!IsRunning)
            {
                IsRunning = true;
                this._intermittentTimer = new Timer(new TimerCallback(TimerCallback), null, _startDelayMs, _runPeriodMs);
            }
        }

        virtual protected void TimerCallback(object sender)
        {
            OnStart();
            lock (this._intermittentTimerSemaphore)
            {
                RunOnce();
            }
            IsRunning = false;
            OnCompleted();
        }

        public override void Dispose()
        {
            this._intermittentTimer.Dispose();
            base.Dispose();
        }
    }

    public class CLongBackgroundTask : CIntermittentBackgroundTask
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="LongBackgroundTask"/> class.
        /// </summary>
        public CLongBackgroundTask()
        {
            StartDelayMs = 50;
            RunPeriodMs = Timeout.Infinite;
        }

        /// <summary>
        /// The timer callback.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        protected override void TimerCallback(object sender)
        {
            OnStart();
            lock (_intermittentTimerSemaphore)
            {
                // we're done with this timer...
                _intermittentTimer.Dispose();
                
                // run this item once...
                RunOnce();

                // no longer running when we get here...
                IsRunning = false;
            }
            OnCompleted();
        }
    }
}
